const APPALACHIAN_STATE_IDLE = 0;
const APPALACHIAN_STATE_LOGIN = 1;
const APPALACHIAN_STATE_ADD = 2;
const APPALACHIAN_STATE_VERIFIED = 3;
const APPALACHIAN_STATE_ACTIVE = 4;
const APPALACHIAN_STATE_WARNING = 5;
const APPALACHIAN_STATE_VERIFYING = 6;
const APPALACHIAN_STATE_CHECK = 7;
const APPALACHIAN_STATE_SUCCESS = 8;
const APPALACHIAN_STATE_FAILURE = 9;
const APPALACHIAN_STATE_ADD_YADIS = 10;

var APPALACHIAN_STATES = {
	"idle" : APPALACHIAN_STATE_IDLE,
	"login" : APPALACHIAN_STATE_LOGIN,
	"add" : APPALACHIAN_STATE_ADD,
	"addyadis" : APPALACHIAN_STATE_ADD_YADIS,
	"verified" : APPALACHIAN_STATE_VERIFIED,
	"active" : APPALACHIAN_STATE_ACTIVE,
	"warning" : APPALACHIAN_STATE_WARNING,
	"verifying" : APPALACHIAN_STATE_VERIFYING,
	"check" : APPALACHIAN_STATE_CHECK,
	"success" : APPALACHIAN_STATE_SUCCESS,
	"failure" : APPALACHIAN_STATE_FAILURE
};

const APPALACHIAN_ACTION_LEAVE = 0;
const APPALACHIAN_ACTION_CLICK = 1;
const APPALACHIAN_ACTION_DETECT_DOM = 2;
const APPALACHIAN_ACTION_DETECT_URL = 3;
const APPALACHIAN_ACTION_OBSERVED_REDIRECT = 4;
const APPALACHIAN_ACTION_VERIFY = 5;
const APPALACHIAN_ACTION_WARN = 6;
const APPALACHIAN_ACTION_STAY = 7;
const APPALACHIAN_ACTION_SUCCEED = 8;
const APPALACHIAN_ACTION_FAIL = 9;
const APPALACHIAN_ACTION_RECORD = 10;
const APPALACHIAN_ACTION_NOOP = 11;
const APPALACHIAN_ACTION_ABORT = 12;
const APPALACHIAN_ACTION_SAVE = 13;
const APPALACHIAN_ACTION_DETECT_YADIS = 14;

var APPALACHIAN_ACTIONS = {
	"leave" : APPALACHIAN_ACTION_LEAVE,
	"click" : APPALACHIAN_ACTION_CLICK,
	"detectdom" : APPALACHIAN_ACTION_DETECT_DOM,
	"detectyadis" : APPALACHIAN_ACTION_DETECT_YADIS,
	"detecturl" : APPALACHIAN_ACTION_DETECT_URL,
	"observedredirect" : APPALACHIAN_ACTION_OBSERVED_REDIRECT,
	"verify" : APPALACHIAN_ACTION_VERIFY,
	"warn" : APPALACHIAN_ACTION_WARN,
	"stay" : APPALACHIAN_ACTION_STAY,
	"succeed" : APPALACHIAN_ACTION_SUCCEED,
	"fail" : APPALACHIAN_ACTION_FAIL,
	"record" : APPALACHIAN_ACTION_RECORD,
	"noop" : APPALACHIAN_ACTION_NOOP,
	"abort" : APPALACHIAN_ACTION_ABORT,
	"save" : APPALACHIAN_ACTION_SAVE
};

var OpenIDUse = function(id, requestor, field, form, method, destination) {
	this.id = id;
	this.requestor = requestor;
	this.field = field;
	this.form = form;
	this.method = method;
	this.destination = destination;
};

var AppalachianFSM = function() {
	this._next = null;
	this._use = null;
};

AppalachianFSM.prototype.init = function() {
	AppalachianDebug.trace("fsm.js", "> AppalachianFSM.init");
	this.doIdle();
	AppalachianDebug.trace("fsm.js", "< AppalachianFSM.init");
}

AppalachianFSM.prototype.storeUse = function(id, requestor, field, form, method, destination) {
	this._use = new OpenIDUse(id, requestor, field, form, method, destination);
};

AppalachianFSM.prototype.deleteUse = function() {
	this._use = null;
}

AppalachianFSM.prototype.commitUse = function() {
	AppalachianDB.recordUse(this._use.id, this._use.requestor, this._use.form, this._use.field);
	this.deleteUse();
};

AppalachianFSM.prototype.getUsedOpenID = function() {
	return (this._use) ? this._use.id : null;
};

AppalachianFSM.prototype.getRequestor = function() {
	return (this._use) ? this._use.requestor : null;
};

AppalachianFSM.prototype.getCurrentState = function() {
	return document.getElementById("appalachian-status-icon").getAttribute("status");
};

AppalachianFSM.prototype.setCurrentState = function(state) {
	document.getElementById("appalachian-status-icon").setAttribute("status", state);
};

AppalachianFSM.prototype.setAction = function(cont) {
	this._next = cont;
}

AppalachianFSM.prototype.advance = function(action) {
	AppalachianDebug.trace("fsm.js", "> AppalachianFSM.advance " + action);
	this._next(action);
};

// doSTATE contract: set the state, then set up a continuation
// that cases depending on the action passed in; any actions that
// fit belong in this continuation, not in the doSTATE body since
// doSTATE may be the next step from several different states.
// 
// You may pass on to the next state from doSTATE if there is only
// one possible exit and it occurs immediately.

AppalachianFSM.prototype.doActive = function() {
	AppalachianDebug.trace("fsm.js", "> AppalachianFSM.doActive");
	this.setCurrentState("active");
	this.setAction(function(action) {
		switch(action) {
		case APPALACHIAN_ACTIONS["click"]:
			AppalachianListener.start(
				this._use.requestor,
				this._use.method,
				this._use.destination
			);
			this.doLogin();
			break;
		case APPALACHIAN_ACTIONS["noop"]:
		case APPALACHIAN_ACTIONS["abort"]:
		case APPALACHIAN_ACTIONS["leave"]:
		default:
			this.doIdle();
			break;
		}
	});
	AppalachianDebug.trace("fsm.js", "< AppalachianFSM.doActive");
};

AppalachianFSM.prototype.doAddAvailable = function() {
	AppalachianDebug.trace("fsm.js", "> AppalachianFSM.doAddAvailable");
	this.setCurrentState("addavailable");
	this.setAction(function(action) {
		switch(action) {
		case APPALACHIAN_ACTIONS["click"]:
			this.doAdd();
			break;
		case APPALACHIAN_ACTIONS["noop"]:
		case APPALACHIAN_ACTIONS["abort"]:
		case APPALACHIAN_ACTIONS["leave"]:
		default:
			this.doIdle();
		}
	});
	AppalachianDebug.trace("fsm.js", "< AppalachianFSM.doAddAvailable");
};

AppalachianFSM.prototype.doAddYadisAvailable = function() {
	AppalachianDebug.trace("fsm.js", "> AppalachianFSM.doAddYadisAvailable");
	this.setCurrentState("addavailable");
	this.setAction(function(action) {
		switch(action) {
		case APPALACHIAN_ACTIONS["click"]:
			this.doAddYadis();
			break;
		case APPALACHIAN_ACTIONS["noop"]:
		case APPALACHIAN_ACTIONS["abort"]:
		case APPALACHIAN_ACTIONS["leave"]:
		default:
			this.doIdle();
		}
	});
	AppalachianDebug.trace("fsm.js", "< AppalachianFSM.doAddYadisAvailable");
};

AppalachianFSM.prototype.doAdd = function() {
	AppalachianDebug.trace("fsm.js", "> AppalachianFSM.doAdd");
	this.setCurrentState("add");
	this.setAction(function(action) {
		switch(action) {
		case APPALACHIAN_ACTIONS["abort"]:
			this.doAddAvailable();
			break;
		case APPALACHIAN_ACTIONS["save"]:
			this.doIdle();
			break;
		default:
			this.doIdle();
		}
	});
	AppalachianDebug.trace("fsm.js", "< AppalachianFSM.doAdd");
};

AppalachianFSM.prototype.doAddYadis = function() {
	AppalachianDebug.trace("fsm.js", "> AppalachianFSM.doAddYadis");
	this.setCurrentState("add");
	this.setAction(function(action) {
		switch(action) {
		case APPALACHIAN_ACTIONS["abort"]:
			this.doAddYadisAvailable();
			break;
		case APPALACHIAN_ACTIONS["save"]:
			this.doIdle();
			break;
		default:
			this.doIdle();
		}
	});
	AppalachianDebug.trace("fsm.js", "< AppalachianFSM.doAdd");
};

// make sure anything going to this state calls this.storeUse()
AppalachianFSM.prototype.doLogin = function() {
	AppalachianDebug.trace("fsm.js", "> AppalachianFSM.doLogin");
	this.setCurrentState("login");
	this.setAction(function(action) {
		switch(action) {
		case APPALACHIAN_ACTIONS["leave"]:
			this.doLoginNext();
			break;
		default:
			this.doIdle();
		}
	});
	AppalachianDebug.trace("fsm.js", "< AppalachianFSM.doLogin");
};

AppalachianFSM.prototype.doLoginNext = function() {
	AppalachianDebug.trace("fsm.js", "> AppalachianFSM.doLoginNext");
	this.setCurrentState("login");
	this.setAction(function(action) {
		switch(action) {
		case APPALACHIAN_ACTIONS["observedredirect"]:
			AppalachianListener.stop();
			this.doVerifying();
			break;
		case APPALACHIAN_ACTIONS["leave"]:
		default:
			this.doIdle();
		}
	});
	AppalachianDebug.trace("fsm.js", "< AppalachianFSM.doLoginNext");
};

AppalachianFSM.prototype.doVerifying = function() {
	AppalachianDebug.trace("fsm.js", "> AppalachianFSM.doVerifying");
	this.setCurrentState("verifying");
	this.setAction(function(action) {
		switch(action) {
		case APPALACHIAN_ACTIONS["verify"]:
			// @@@ this really belongs in succeed, but
			// we aren't using the success checking code yet;
			// remove when properly in place
			this.commitUse();
			this.doVerified();
			break;
		case APPALACHIAN_ACTIONS["warn"]:
			this.doWarning();
			break;
		default:
			this.doVerifying();
		}
	});
	AppalachianDebug.trace("fsm.js", "< AppalachianFSM.doVerifying");
};

AppalachianFSM.prototype.doVerified = function() {
	AppalachianDebug.trace("fsm.js", "> AppalachianFSM.doVerified");
	this.setCurrentState("verified");
	this.setAction(function(action) {
		// @@@ no checking done for now, rather complex to track it
		// this.doCheck();
		this.doIdle();
	});
	AppalachianDebug.trace("fsm.js", "< AppalachianFSM.doVerified");
};

AppalachianFSM.prototype.doWarning = function() {
	AppalachianDebug.trace("fsm.js", "> AppalachianFSM.doWarning");
	this.setCurrentState("warning");
	this.setAction(function(action) {
		switch(action) {
		case APPALACHIAN_ACTIONS["leave"]:
			this.doIdle();
			break;
		case APPALACHIAN_ACTIONS["stay"]:
			this.doIgnoreWarning();
			break;
		}
	});
	AppalachianDebug.trace("fsm.js", "< AppalachianFSM.doWarning");
};

AppalachianFSM.prototype.doIgnoreWarning = function() {
	AppalachianDebug.trace("fsm.js", "> AppalachianFSM.doIgnoreWarning");
	this.setCurrentState("ignorewarning");	
	this.setAction(function(action) {
		this.doIdle();
	});
	AppalachianDebug.trace("fsm.js", "< AppalachianFSM.doIgnoreWarning");
};

// @@@ this does nothing; put the success/failure check in Appalachian
AppalachianFSM.prototype.doCheck = function() {
	AppalachianDebug.trace("fsm.js", "> AppalachianFSM.doCheck");
	this.setCurrentState("check");
	this.setAction(function(action) {
		switch(action) {
		case APPALACHIAN_ACTIONS["succeed"]:
			this.commitUse();
			this.doSuccess();
			break;
		case APPALACHIAN_ACTIONS["fail"]:
			this.deleteUse();
			this.doFailure();
			break;
		}
	});
	AppalachianDebug.trace("fsm.js", "< AppalachianFSM.doCheck");
};

AppalachianFSM.prototype.doSuccess = function() {
	AppalachianDebug.trace("fsm.js", "> AppalachianFSM.doSuccess");
	this.setCurrentState("success");
	this.doIdle();
	AppalachianDebug.trace("fsm.js", "< AppalachianFSM.doSuccess");
};

AppalachianFSM.prototype.doFailure = function() {
	AppalachianDebug.trace("fsm.js", "> AppalachianFSM.doFailure");
	this.setCurrentState("failure");
	this.doIdle();
	AppalachianDebug.trace("fsm.js", "< AppalachianFSM.doFailure");
};

AppalachianFSM.prototype.doIdle = function() {
	AppalachianDebug.trace("fsm.js", "> AppalachianFSM.doIdle");
	if (AppalachianCommands.apiLogin) {
		AppalachianCommands.apiLogin();
		AppalachianCommands.apiLogin = false;
	}
	this.setCurrentState("idle");
	this.setAction(function(action) {
		switch(action) {
		case APPALACHIAN_ACTIONS["detectdom"]:
			this.doAddAvailable();
			break;
		case APPALACHIAN_ACTIONS["detectyadis"]:
			this.doAddYadisAvailable();
			break;
		case APPALACHIAN_ACTIONS["detecturl"]:
			this.doActive();
			break;
		case APPALACHIAN_ACTIONS["click"]:
			AppalachianListener.start(this._use.requestor, this._use.method, this._use.destination);
			this.doLogin();
			break;
		case APPALACHIAN_ACTIONS["noop"]:
		case APPALACHIAN_ACTIONS["abort"]:
		case APPALACHIAN_ACTIONS["leave"]:
		default:
			this.doIdle();
		}
	});
	AppalachianDebug.trace("fsm.js", "< AppalachianFSM.doIdle");
};
